//
//  ZArchivePrivate.h
//  Part of Z-Way.C library
//
//  Created by Pavel Kulaha.
//  Based on Z-Way source code written by Christian Paetz and Poltorak Serguei
//
//  Copyright (c) 2024 Z-Wave.Me
//  All rights reserved
//  info@z-wave.me
//
//  This source file is subject to the terms and conditions of the
//  Z-Wave.Me Software License Agreement which restricts the manner
//  in which it may be used.
//

#ifndef zarchive_private_h
#define zarchive_private_h

#include "ZPlatform.h"
#include "ZLog.h"
#include "ZFileOperationsPrivate.h"
#include "ZByteOperationsPrivate.h"
#include "ZStreamPrivate.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef struct __ZArchiveGzUnpackTree_s
{
    ZWWORD table[16]; // table of code length counts
    ZWWORD trans[288]; // code -> symbol translation table
} __ZArchiveGzUnpackTree_t;

typedef enum __ZArchiveGzUnpackState_e
{
    __ZArchiveGzUnpackStateSkipBytes,
    __ZArchiveGzUnpackStateSkipString,
    __ZArchiveGzUnpackStateRingBuffer,
    __ZArchiveGzUnpackStateHeader,
    __ZArchiveGzUnpackStateHeaderFlagExtra,
    __ZArchiveGzUnpackStateHeaderFlagName,
    __ZArchiveGzUnpackStateHeaderFlagComment,
    __ZArchiveGzUnpackStateHeaderFlagHcrc,
    __ZArchiveGzUnpackStateTrees,
    __ZArchiveGzUnpackStateTreesCodelength,
    __ZArchiveGzUnpackStateTreesLoop,
    __ZArchiveGzUnpackStateTreesBits,
    __ZArchiveGzUnpackStateBlock,
    __ZArchiveGzUnpackStateBlockInflate,
    __ZArchiveGzUnpackStateBlockInflateLengthCode,
    __ZArchiveGzUnpackStateBlockInflateDist,
    __ZArchiveGzUnpackStateBlockInflateOffset,
    __ZArchiveGzUnpackStateBlockSymbol,
    __ZArchiveGzUnpackStateBlockUncompressed,
    __ZArchiveGzUnpackStateBlockUncompressedProcess,
    __ZArchiveGzUnpackStateBlockFinal,
    __ZArchiveGzUnpackStateBlockFinalError,
    __ZArchiveGzUnpackStateBlockFinalResult,
} __ZArchiveGzUnpackState_t;

typedef struct __ZArchiveGzUnpackSkip_s
{
    size_t n;
} __ZArchiveGzUnpackSkip_t;

typedef struct __ZArchiveBit_s
{
    ZWBYTE bitcount;
    ZWBYTE bits;
} __ZArchiveBit_t;

typedef enum __ZArchiveGzUnpackViewBlock_e
{
    __ZArchiveGzUnpackViewBlockUncompressed = 0,
    __ZArchiveGzUnpackViewBlockFixed,
    __ZArchiveGzUnpackViewBlockDynamic,
} __ZArchiveGzUnpackViewBlock_t;

typedef struct __ZArchiveGzUnpackTypeBlockSub_s
{
    ZWBYTE final : 1;
    __ZArchiveGzUnpackViewBlock_t view : 2;
} __ZArchiveGzUnpackTypeBlockSub_t;

typedef union __ZArchiveGzUnpackTypeBlock_s
{
    ZWBYTE common;
    __ZArchiveGzUnpackTypeBlockSub_t sub;
} __ZArchiveGzUnpackTypeBlock_t;

typedef struct __ZArchiveGzUnpackDecodeSymbol_s
{
    const __ZArchiveGzUnpackTree_t *table;
    int sum;
    int cur;
    __ZArchiveGzUnpackState_t state_next;
    ZWWORD res;
    ZWBYTE len;
    ZWBOOL ready;
} __ZArchiveGzUnpackDecodeSymbol_t;

typedef struct __ZArchiveGzUnpackDecode_s
{
    const __ZArchiveGzUnpackTree_t *lt;
    const __ZArchiveGzUnpackTree_t *dt;
    ZWWORD length_code;
    union
    {
        ZWBYTE sym;
        ZWBYTE dist;
    };
} __ZArchiveGzUnpackDecode_t;

typedef struct __ZArchiveGzUnpackTrees_s
{
    ZWWORD hlit;
    ZWWORD hlimit;
    ZWBYTE lengths[288 + 32];
    ZWBYTE hdist;
    ZWBYTE hclen;
    ZWBYTE fill_value;
    ZWBYTE lbits;
    ZWBYTE lbase;
} __ZArchiveGzUnpackTrees_t;

typedef struct _ZArchiveGzUnpackCtx_s
{
    ZWLog log;
    const _ZStreamRW_t *stream;
    void *ctx_stream;
    const ZWBYTE *read_data_b;
    const ZWBYTE *read_data_e;
    
    ZWDWORD crc32zlib;
    
    _ZByteRingBuffer_t ring;
    ZWBYTE ring_buffer[10];
    ZWBYTE ring_target;
    
    __ZArchiveGzUnpackState_t state;
    __ZArchiveGzUnpackState_t state_next;
    ZWBYTE flags;
    __ZArchiveBit_t bit;
    __ZArchiveGzUnpackSkip_t skip;
    __ZArchiveGzUnpackTypeBlock_t type_block;
    __ZArchiveGzUnpackViewBlock_t last_view;
    __ZArchiveGzUnpackDecodeSymbol_t decode_symbol;
    __ZArchiveGzUnpackDecode_t decode;
    __ZArchiveGzUnpackTrees_t trees;
    
    __ZArchiveGzUnpackTree_t ltree;
    __ZArchiveGzUnpackTree_t dtree;
    
    ZWWORD length_block;
    
     ZWBOOL fixed_trees;
} _ZArchiveGzUnpackCtx_t;

#define ZARCHIVE_GZ_PACK_HASH_BITS 8

typedef struct _ZArchiveGzPackCtxWindow_s
{
    size_t i;
    ZWBYTE buffer[1024 * 2]; // max 32768
} _ZArchiveGzPackCtxWindow_t;

typedef struct _ZArchiveGzPackCtxBit_s
{
    ZWDWORD bits;
    ZWBYTE nbits;
} _ZArchiveGzPackCtxBit_t;

typedef struct __ZArchiveGzPackCtxEnd_s
{
    ZWDWORD zlibcrc32;
    ZWDWORD sourcelength;
} __ZArchiveGzPackCtxEnd_t;

typedef struct _ZArchiveGzPackCtx_s
{
    ZWLog log;
    const _ZStreamWriter_t *stream;
    void *ctx_stream;
    __ZArchiveGzPackCtxEnd_t end;
    _ZArchiveGzPackCtxWindow_t window;
    _ZArchiveGzPackCtxBit_t bit;
    ZWWORD hash_table[1 << ZARCHIVE_GZ_PACK_HASH_BITS];
} _ZArchiveGzPackCtx_t;

typedef struct _ZArchiveTarHeader_s
{
    ZWCHAR name[100];
    ZWCHAR mode[8];
    ZWCHAR uid[8];
    ZWCHAR gid[8];
    ZWCHAR size[12];
    ZWCHAR mtime[12];
    ZWCHAR chksum[8];
    ZWCHAR typeflag;
    ZWCHAR linkname[100];
    ZWCHAR magic[6];
    ZWCHAR version[2];
    ZWCHAR uname[32];
    ZWCHAR gname[32];
    ZWCHAR devmajor[8];
    ZWCHAR devminor[8];
    ZWCHAR prefix[155];
    ZWCHAR padding[12];
} _ZArchiveTarHeader_t; // ISO/IEC 9945-1: 1996 ANSI/IEEE Std 1003.1, 1996 Edition

typedef struct _ZArchiveTarPackCtx_s
{
    ZWLog log;
    const _ZStreamWriter_t *stream;
    void *ctx_stream;
    _ZArchiveTarHeader_t header;
    _ZFileOperationsLstat_t file_info;
    ZWError err;
    ZWCSTR name;
} _ZArchiveTarPackCtx_t;

typedef struct _ZArchiveTarUnPackProcess_s
{
    ZWDWORD64 length_file; // max size in tar is 12 digits in octal, so 36 bits. Selecting the closest bigger type.
    const _ZStreamReader_t *stream;
    void *ctx_stream;
    size_t length_path;
    _ZFileOperationsTypeFile_t type;
    ZWCHAR path[Z_SIZE_FIELD_STRUCT(_ZArchiveTarHeader_t, name) + Z_SIZE_FIELD_STRUCT(_ZArchiveTarHeader_t, prefix) + 1]; // +1 -'\0'
} _ZArchiveTarUnPackProcess_t;

typedef struct _ZArchiveTarUnPackCtx_s
{
    ZWDWORD64 offset;
    ZWDWORD64 size;
    void *arg;
    ZWBOOL (*filter)(void *const arg, const ZWCSTR path, const size_t length);
    ZWLog log;
    _ZArchiveTarUnPackProcess_t process;
    _ZArchiveTarHeader_t header;
} _ZArchiveTarUnPackCtx_t;

ZWEXPORT_PRIVATE const _ZStreamWriter_t *_zarchive_stream_write_gz(void);
ZWEXPORT_PRIVATE ZWError _zarchive_gz_pack_init(_ZArchiveGzPackCtx_t *const ctx, const ZWLog log, const _ZStreamWriter_t *const stream, void *const ctx_stream);
ZWEXPORT_PRIVATE ZWError _zarchive_gz_pack(_ZArchiveGzPackCtx_t *const ctx, const void *const ptr, const size_t n);
ZWEXPORT_PRIVATE ZWError _zarchive_gz_pack_end(_ZArchiveGzPackCtx_t *const ctx);
static inline void _zarchive_gz_pack_free(_ZArchiveGzPackCtx_t *const UNUSED(ctx))
{
}

ZWEXPORT_PRIVATE const _ZStreamRW_t *_zarchive_stream_gz_unpack(void);
ZWEXPORT_PRIVATE void _zarchive_gz_unpack_init(_ZArchiveGzUnpackCtx_t *const ctx, const ZWLog log, const _ZStreamRW_t *const stream, void *const ctx_stream);
ZWEXPORT_PRIVATE ZWError _zarchive_gz_unpack(_ZArchiveGzUnpackCtx_t *const ctx, const void *const data, const size_t n);
static inline void _zarchive_gz_unpack_free(_ZArchiveGzUnpackCtx_t *const UNUSED(ctx))
{
}

static inline ZWError _zarchive_gz_unpack_end(_ZArchiveGzUnpackCtx_t *const ctx)
{
    return (ctx->state == __ZArchiveGzUnpackStateBlockFinalResult) ? NoError : BadData;
}

static inline void _zarchive_tar_pack_init(_ZArchiveTarPackCtx_t *const ctx, const ZWLog log, const _ZStreamWriter_t *const stream, void *const ctx_stream)
{
    ctx->log = log;
    ctx->stream = stream;
    ctx->ctx_stream = ctx_stream;
};
ZWEXPORT_PRIVATE ZWError _zarchive_tar_pack_path(_ZArchiveTarPackCtx_t *const ctx, const ZWCSTR path, const ZWCSTR name);
ZWEXPORT_PRIVATE ZWError _zarchive_tar_pack_path_not_found(_ZArchiveTarPackCtx_t *const ctx, const ZWCSTR path, const ZWCSTR name);

ZWEXPORT_PRIVATE void _zarchive_tar_unpack_init(_ZArchiveTarUnPackCtx_t *const ctx, const ZWLog log, const _ZStreamReader_t *const stream, void *const ctx_stream, void *const arg, ZWBOOL (*const filter)(void *const arg, const ZWCSTR path, const size_t length));
ZWEXPORT_PRIVATE ZWError _zarchive_tar_unpack_next(_ZArchiveTarUnPackCtx_t *const ctx);
static inline const _ZArchiveTarUnPackProcess_t *_zarchive_tar_unpack_process(_ZArchiveTarUnPackCtx_t *const ctx)
{
    return &ctx->process;
}
ZWEXPORT_PRIVATE ZWError _zarchive_tar_unpack_get_timestamp(_ZArchiveTarUnPackCtx_t *const ctx, time_t *const p_time);
ZWEXPORT_PRIVATE ZWError _zarchive_tar_unpack_get_mode(_ZArchiveTarUnPackCtx_t *const ctx, mode_t *const p_mode);
ZWEXPORT_PRIVATE ZWError _zarchive_tar_unpack(_ZArchiveTarUnPackCtx_t *const ctx, const _ZByteData_t *const outpath); // After this, it is forbidden to extract information about the file.

ZWEXPORT_PRIVATE ZWError _zarchive_tar_extract(const ZWLog log, const ZWCSTR path, const _ZStreamReader_t *const stream, void *const ctx_stream, void *const arg, ZWBOOL (*const filter)(void *const arg, const ZWCSTR path, const size_t length));

#ifdef __cplusplus
}
#endif

#endif // zarchive_private_h
